<template>
  <ElPopover
    ref="popover"
    popper-class="fa-widget-popover fa-contract-scope"
    :placement="widget.isRiding ? 'left-start' : 'right-start'"
    :reference="reference"
    @after-leave="afterClose"
  >
    <div class="fa-batch-apply">
      <header class="fa-widget-popover__hd">{{ title }}</header>

      <div class="fa-batch-apply__alert">
        <i class="fa-batch-apply__alert-icon fa-icon-info-solid"></i>
        <div class="fa-batch-apply__alert-desc">
          应用后该批控件将加盖同一个签章；文档仅单页时不可设置骑缝章；文档只有两或三页时无法设置骑缝章至奇偶页
        </div>
      </div>

      <h3 class="fa-batch-apply__title">应用文档 ({{ count }})</h3>

      <ElTable
        ref="tableRef"
        class="fa-batch-apply__grid"
        size="mini"
        :data="table.data"
        max-height="220"
        style="min-height: 140px"
        :row-class-name="rawClass"
      >
        <ElTableColumn type="selection" width="40" :selectable="selectable" />

        <ElTableColumn prop="name" label="文档名称" show-overflow-tooltip>
          <template #default="{ row }">
            <Component :is="table.isCurrent(row) ? 'b' : 'span'">
              {{ row.name }}
            </Component>
          </template>
        </ElTableColumn>

        <ElTableColumn prop="count" label="文档页数" width="100">
          <template #default="{ row }">{{ row.pages.length }} 页</template>
        </ElTableColumn>
      </ElTable>

      <h3 class="fa-batch-apply__title">应用页面</h3>

      <ElForm ref="form" :model="model" :rules="rules">
        <ElFormItem prop="applyType">
          <ElSelect v-model="model.applyType">
            <ElOption
              v-for="({ label, value }, index) in applyOptions"
              :key="index"
              :label="label"
              :value="value"
              :disabled="!!getApplyOptionDisableTips(value)"
              :title="getApplyOptionDisableTips(value)"
            />
          </ElSelect>
        </ElFormItem>

        <ElFormItem v-if="isCustomApply && !widget.isRiding" prop="applyPage">
          <ElInput
            ref="applyPageRef"
            v-model="model.applyPage"
            clearable
            maxlength="1000"
            placeholder="请输入页码或页码范围以逗号分隔，如1, 2, 5-8"
            @input="filterInput"
            @blur="filterBlur"
          />
        </ElFormItem>
      </ElForm>

      <footer class="fa-batch-apply__ft">
        <ElButton size="mini" @click="close">取消</ElButton>

        <ElButton type="primary" size="mini" :disabled="!count" @click="submit">确定</ElButton>
      </footer>
    </div>
  </ElPopover>
</template>

<script lang="ts">
import { computed, defineComponent, inject, onMounted, PropType, reactive, ref, toRef, toRefs, watch } from 'vue'
import { Table, Form, Message } from 'element-ui'
import { ApplyPage, DocData, Viewer, WidgetKind } from '@/types'
import { Widget } from '@/models'
import { createId, processData } from '@/utils'
import {
  batchPageInputFilter,
  batchPageBlurFilter,
  batchPageArrange,
  batchPageFormat,
  batchPageFilter
} from '@/models/widget/batch'
import usePopover from '@/utils/compositions/usePopover'
import { ElInput } from 'element-ui/types/input'
import { getDocPageSum } from '../contract/utils'

interface ElTable extends Table {
  /** 选中数据 */
  selection: []
}

/** 应用页类型 */
enum ApplyType {
  /** 全部页面 */
  ALL = 'ALL',

  /** 奇数页面 */
  ODD = ApplyPage.ODD,

  /** 偶数页面 */
  EVEN = ApplyPage.EVEN,

  /** 指定页面 */
  CUSTOM = 'CUSTOM'
}

/** 应用页类型选项 */
const ApplyTypeMap = new Map([
  [
    ApplyType.ALL,
    {
      label: '全部页面',
      value: ApplyType.ALL,
      initial: undefined
    }
  ],
  [
    ApplyType.ODD,
    {
      label: '奇数页面',
      value: ApplyType.ODD,
      initial: ApplyType.ODD
    }
  ],
  [
    ApplyType.EVEN,
    {
      label: '偶数页面',
      value: ApplyType.EVEN,
      initial: ApplyType.EVEN
    }
  ],
  [
    ApplyType.CUSTOM,
    {
      label: '指定页面',
      value: ApplyType.CUSTOM,
      initial: ''
    }
  ]
])

/** 表格 */
function useTable(props: { widget: Widget }) {
  /** 合同控件 */
  const viewer = inject('Viewer') as Viewer

  /** 表格控件 */
  const table = ref(null as ElTable | null)

  /** 当前选中数据 */
  const selected = computed<DocData[]>(() => table.value?.selection ?? [])

  /** 选择文档 */
  function select(docs: DocData[]) {
    docs.forEach(item => table.value?.toggleRowSelection?.(item, true))
  }

  /** 默认选中文档 */
  onMounted(() => {
    const linked = props.widget.linkDocs

    linked.length && select(linked)
  })

  return reactive({
    /** 表格控件 */
    ref: table,

    /** 列表数据 */
    data: computed(() => viewer.docs),

    /** 当前选中数据 */
    selected,

    /** 当前选中数量 */
    count: computed(() => selected.value.length),

    /** 是否当前文档 */
    isCurrent(doc: DocData) {
      return viewer.doc === doc
    }
  })
}

/** 应用页面 */
function useApplyPage(props: { widget: Widget }) {
  /** 表单 */
  const form = ref<Form | null>(null)

  /** 表单数据 */
  const model = reactive({
    /** 应用页类型 */
    applyType: ApplyType.ALL,

    /** 指定页 */
    applyPage: ''
  })

  /** 是否自定义 */
  const isCustomApply = computed(() => model.applyType === ApplyType.CUSTOM)

  /** 指定页输入框 */
  const applyPageRef = ref<ElInput | null>(null)

  /** 指定页输入框显示后自动聚焦 */
  watch(applyPageRef, input => input?.focus())

  /** 初始数据 */
  function init(originPage?: string) {
    if ([ApplyPage.ODD, ApplyPage.EVEN].includes(originPage as ApplyPage)) {
      // 奇偶数页
      model.applyType = originPage as ApplyType
    } else if (/\d/.test(originPage || '')) {
      // 存在数据则自定义页
      model.applyType = ApplyType.CUSTOM
      model.applyPage = batchPageFilter(originPage || '')
    } else {
      model.applyType = ApplyType.ALL
    }
  }

  // 初始指定页
  onMounted(() => init(props.widget.applyPage))

  return reactive({
    /** 表单 */
    form,

    /** 表单数据 */
    model,

    /** 是否自定义 */
    isCustomApply,

    /** 指定页输入框 */
    applyPageRef,

    /** 指定页面选项 */
    applyOptions: computed(() => {
      const options = [...ApplyTypeMap.values()]

      // 骑缝控件不支持自定义
      if (props.widget.isRiding) return options.filter(o => o.value !== ApplyType.CUSTOM)

      return options
    }),

    /** 获取指定页值 */
    getApplyPage() {
      const { applyType, applyPage } = model

      return isCustomApply.value
        ? // 指定页去除空格
          applyPage.replace(/\s+/g, '')
        : // 非指定页默认值
          ApplyTypeMap.get(applyType)?.initial
    },

    /** 输入过滤 */
    filterInput() {
      model.applyPage = processData(model.applyPage, batchPageInputFilter, batchPageFormat)
    },

    /** 失焦过滤 */
    filterBlur() {
      model.applyPage = processData(model.applyPage, batchPageBlurFilter, batchPageArrange, batchPageFormat)
    }
  })
}

/** 批量应用 */
function useBatchApply(props: { widget: Widget }) {
  /** 合同控件 */
  const viewer = inject('Viewer') as Viewer

  /** 更新控件批量应用信息 */
  function updateWidget(applyPage?: string) {
    const { widget } = props

    // 普通控件转为批量
    if (widget.kind === WidgetKind.NORMAL) {
      widget.kind = WidgetKind.BATCH
    }

    // 更新关联 id 和指定页数据
    Object.assign(widget, {
      linkId: widget.linkId || createId(),
      applyPage
    } as Partial<Widget>)

    // 更新页码为首个所在页面
    widget.p = widget.pages[0]
  }

  /** 批量应用选中文档 */
  function applySelected(selected: DocData[]) {
    const { widget } = props

    if (!widget) return

    const { docs, ridings, batches } = viewer
    const { docId, isRiding, linkId } = widget
    const widgets = isRiding ? ridings : batches

    docs.forEach(doc => {
      // 当前文档 id
      const dId = doc.id

      // 是否已选中
      const isSelect = selected.includes(doc)

      // 跳过当前控件所在页面
      if (dId === docId && isSelect) return

      // 关联的控件
      const links = widgets.filter(w => w.docId === dId && w.linkId === linkId)

      // 未选择文档时，移除已存在关联控件
      if (!isSelect) {
        if (links.length) viewer.removeWidgets(links)
        return
      }

      // 已选择文档不存在关联控件，添加关联控件
      if (!links.length) {
        const clone = widget.clone()

        clone.docId = dId
        clone.linkId = linkId

        viewer.addWidget(clone)
      }
    })

    widget.adjust()

    widget.syncLinkWidgets()
  }

  return {
    /** 更新控件批量应用信息 */
    updateWidget,

    /** 批量应用选中文档 */
    applySelected
  }
}

/** 批量应用 */
export default defineComponent({
  name: 'FaBatchApply',

  props: {
    /** 标题 */
    title: {
      type: String,
      default: '批量应用'
    },

    /** 触发节点 */
    reference: {
      type: HTMLElement as PropType<HTMLElement | null>
    },

    /** 控件 */
    widget: {
      type: Widget,
      required: true
    }
  },

  setup(props, { emit }) {
    /** 合同控件 */
    const viewer = inject('Viewer') as Viewer

    /** 弹出层 */
    const popover = usePopover(emit)

    /** 表格 */
    const table = useTable(props)

    /** 应用页面 */
    const apply = useApplyPage(props)

    /** 批量应用 */
    const batch = useBatchApply(props)

    /** 文档禁用提示 */
    function getDocDisableTips(doc: DocData) {
      const { isRiding } = props.widget
      const type = apply.model.applyType
      const count = doc.pages.length

      if (isRiding) {
        if (count === 1) return '文档仅单页时不可设置骑缝章'

        if (
          // 骑缝控件 2 页文档不允许奇偶页
          (count === 2 && [ApplyType.ODD, ApplyType.EVEN].includes(type)) ||
          // 骑缝控件 3 页文档不允许偶数页
          (count === 3 && type === ApplyType.EVEN)
        )
          return '文档只有两或三页时无法设置骑缝章至奇偶页'
      }

      // 1 页文档不允许偶数页
      if (count === 1 && type === ApplyType.EVEN) return '文档只有一页时无法批量应用至偶数页'

      return ''
    }

    return {
      ...toRefs(popover),

      /** 文档列表 */
      table,

      /** 表格组件实例 */
      tableRef: toRef(table, 'ref'),

      /** 选中数量 */
      count: toRef(table, 'count'),

      /** 文档是否可选择 */
      selectable: (doc: DocData) => !getDocDisableTips(doc),

      /** 文档是否可选择 */
      rawClass: ({ row }: { row: DocData }) => (getDocDisableTips(row) ? 'is-disabled' : ''),

      ...toRefs(apply),

      /** 指定页面选项禁用提示 */
      getApplyOptionDisableTips(type: ApplyType) {
        const { isRiding } = props.widget
        const { selected } = table

        if (
          isRiding &&
          // 骑缝控件 2 页文档不允许奇偶页
          ((selected.some(doc => doc.pages.length === 2) && [ApplyType.ODD, ApplyType.EVEN].includes(type)) ||
            // 骑缝控件 3 页文档不允许偶数页
            (selected.some(doc => doc.pages.length === 3) && type === ApplyType.EVEN))
        )
          return '文档只有两或三页时无法设置骑缝章至奇偶页'

        // 1 页文档不允许偶数页
        if (selected.some(doc => doc.pages.length === 1) && type === ApplyType.EVEN)
          return '文档只有一页时无法批量应用至偶数页'

        return ''
      },

      /** 表单校验 */
      rules: {
        applyPage: [
          { required: true, message: '请输入页码或页码范围', trigger: 'blur' },
          {
            validator(rule: object, value: string, callback: (error?: Error) => void) {
              /** 选中文档页码数 */
              const lengths = table.selected.map(d => d.pages.length)

              /** 最大页码 */
              const max = Math.max(...lengths)

              /** 指定页码数值 */
              const values = value.split(/\D+/).map(v => +v)

              if (
                // 指定页码超出最大页码
                values.some(v => v > max) ||
                // 选中文档中不包含所有指定页码
                lengths.some(c => !values.some(p => p <= c))
              )
                callback(new Error('页面范围已超过文档最大页数，请重新设置'))

              callback()
            },
            trigger: 'blur'
          }
        ]
      },

      /** 提交 */
      async submit() {
        await apply.form?.validate()

        const applyPage = apply.getApplyPage()
        const selected = table.selected

        batch.updateWidget(applyPage)

        batch.applySelected(selected)

        Message.success('批量应用成功')

        viewer.$emit('batch-apply', props.widget, selected, applyPage)

        popover.close()
      }
    }
  }
})
</script>

<style lang="scss">
.fa-batch-apply {
  box-sizing: border-box;
  width: 440px;

  .fa-widget-popover__hd {
    border-bottom: 1px solid #e9e9e9;
  }

  // 警告
  &__alert {
    display: flex;
    margin-top: 16px;
    padding: 4px 16px;
    background-color: $color-primary-lightest;
    border-radius: 4px;

    &-icon {
      margin-top: 2px;
      margin-right: 8px;
      color: $color-primary;
      font-size: 16px;
    }

    &-desc {
      color: $color-text-primary;
      font-size: 12px;
      line-height: 1.5;
    }
  }

  &__title {
    height: 36px;
    margin: 0;
    font-weight: 700;
    font-size: 12px;
    line-height: 36px;
  }

  &__grid {
    &.el-table {
      th.el-table__cell {
        background: $background-color-regular;
      }

      .el-table__row.is-disabled .cell {
        color: $color-text-disabled;
        cursor: not-allowed;
      }
    }
  }

  &__ft {
    padding-top: 16px;
    text-align: right;
  }

  .el-form-item {
    margin-bottom: 8px;
  }
}
</style>
